“遗传算法”解决“背包问题”

遗传算法基本思想:

1)       一个种群有多个个体,每个个体有染色体和对应的基因

为了繁殖进行:

2)       选择:在残酷的世界中,适者生存,优胜略汰。

3)       重组:染色体交叉,基因重组

4)       突变:染色体上的基因小概率的突变 (一般给小数点后两位)

 

背包问题:

背包只能容得下一定重量b的物品,物品有m种,每种物品有自己的重量w(i)和价值v(i)(0<i<=m),从这些物品中选择装入背包,是背包不超过重量b,但价值又要最大。

 

运用动态规划,分支限界都可以达到效果,但不佳。

我用遗传算法解决:

一般人有多条染色体,但对于背包问题,一个解我们将看成一个个体,所以,一个个体只有一个染色体,一个染色体对应多个基因。如:100101010100111 表示装入背包的可能解。(具体情况具体分析)

 

遗传所做准备:

1)       用0表示“不选择装入”,1表示“装入”,形成一条基因链;100101010100111则表示“15种物品”装入或不装入背包的可能解。                         ------- 此处用chrom[]存放基因,代表染色体

2)       一个基因对应一个个体。                                                              ------- 此处用Population类或结构体声明其含有chrom[]等信息

3)       可能的解有很多,构成一个种群。                                           ------- 用Population类定义一个数组代表个体构成的种群newPop[]:存放新生代,oldPop[]:存放上一代

4)       适应度:适应度和目标函数是正相关的,所以需要物品价值和重量。

------- fitness,weight包含在Population类中

最大适应度:maxFitness,

最小适应度:minFitness,

总适应度:sumFitness,(帮助求突变和交叉的染色体)

平均适应度:avgFitness

 

 

遗传算法的函数:

基本:

1)       InitPop()                      初始化个体,使每个个体都有基因组

2)       Statistics(*pop)                   计算适应度(最大,最小,总的,平均的)

3)       Selection(*pop)          通过选择种群中符合要求的父母去繁殖新代,返回这对父母的位置

4)       crossover(*parent1,*parent2,pos)  传入要改的个体位置,随机产生交叉位置,用优良父母繁殖优良后代并替代传入个体位置

5)       mutation(i)                  i为基因组基因的位置,逐个基因看是否要变异

6)       generation()               对个体进行判断,若不符合要求,进行选择,重组,突变。

背包的适应性函数:(个体是一个解)

1)       calWeight(pop)          计算个体的重量

2)       calFit( pop )                 计算个体的价值

 

  1 #include <AfxWin.h>
  2 #include <stdlib.h>
  3 #include <math.h>
  4 #include <time.h>
  5 #include <conio.h>
  6 #include <stdio.h>
  7 /**
  8  * @author:     ckj
  9  * @date:         2012/12/27 
 10  */
 11 /*============= 一些必要的常量 ===============*/
 12 #define POP_SIZE 200            // 种群的规模
 13 #define PRO_CROSS 0.618            // 交叉概率
 14 #define PRO_MUTATE 0.03            // 变异概率
 15 #define CHROM_SIZE 50            // 染色体长度
 16 #define GENERATION_NUM 1000        // 繁殖代数
 17 /*============================================*/
 18 
 19 // 个体类
 20 struct population{
 21     unsigned int chrom[CHROM_SIZE];        // 基因组
 22     double weight;                        // 背包重量
 23     double fitness;                        // 适应度
 24     unsigned int parent1,parent2,cross;    // 双亲,交叉节点
 25 };
 26 
 27 // 新生代和上一代的种群
 28 struct population oldPop[POP_SIZE], newPop[POP_SIZE];
 29 
 30 // 物体的重量,收益,背包的容量
 31 int weight[CHROM_SIZE], profit[CHROM_SIZE], contain;
 32 
 33 // 种群的总的适应度,最小,最大,平均适应度。
 34 double sumFitness, minFitness, maxFitness, avgFitness;
 35 
 36 // 计算适应度时,用的惩罚函数系数
 37 double alpha;
 38 
 39 // 一个种群的最大和最小适应度个体
 40 int minPop, maxPop;
 41 
 42 /**=============================================
 43  * 参数:chr为装入背包的一个可能解
 44  * 用途:计算装入背包的一个可能解得(个体)重量
 45  * 返回值:个体重量
 46  ============================================**/
 47 double calWeight( unsigned int *chr ){
 48     double popSumWeight = 0;
 49     for(int i = 0; i < CHROM_SIZE; i++){
 50         popSumWeight += (*chr) * weight[i];
 51         chr++;
 52     }
 53     return popSumWeight;
 54 }
 55 
 56 /**===============================
 57  * 参数:chr为装入背包的一个可能解
 58  * 用途:计算装入背包的总价值
 59  * 返回值:返回个体的价值
 60  ================================*/
 61 double calFit( unsigned int *chr ){
 62     double popProfit = 0;
 63     for(int i = 0; i < CHROM_SIZE; i++){
 64         popProfit += (*chr) * profit[i];
 65         chr++;
 66     }
 67     return popProfit;
 68 }
 69 
 70 /**=======================================
 71  * 参数:传入一个种群
 72  * 用途:计算种群的最大适应度和最小适应度
 73  =======================================*/
 74 void statistics( struct population *pop ){
 75     double tmpFitness;
 76 
 77     sumFitness = pop[0].fitness;
 78     minFitness = pop[0].fitness;
 79     minPop = 0;
 80     maxFitness = pop[0].fitness;
 81     maxPop = 0;
 82 
 83     for( int i =1; i < POP_SIZE; i++ ){
 84     // 计算种群的总适应度
 85         sumFitness += pop[i].fitness;
 86         tmpFitness = pop[i].fitness;
 87         // 选择最大适应度的个体的位置和适应度的值
 88         if( (tmpFitness > maxFitness) && ((int)(tmpFitness*10)%10 == 0) ){
 89             maxFitness = pop[i].fitness;
 90             maxPop = i;
 91         }
 92         // 选择最小适应度的个体的位置
 93         if( (tmpFitness < minFitness) ){
 94             minFitness = pop[i].fitness;
 95             minPop = i;
 96         }    
 97     avgFitness = sumFitness / (float)POP_SIZE;
 98     }    
 99 }
100 
101 /**====================
102  * 用途:报告种群信息 *
103  ====================**/
104 void report( struct population *pop, int gen ){
105     int popWeight = 0;
106     printf("\nThe generation is %d.\n", gen);        // 输出种群的代数
107     // 输出种群中最大适应度个体的染色体信息
108     printf("The population chrom is: \n");
109     for( int j = 0; j < CHROM_SIZE; j++ ){
110         if( j % 5 == 0 ) {printf(" ");}
111         // 每个基因单位输出
112         printf("%1d", pop[maxPop].chrom[j]);
113     }
114     printf("\nThe population's max fitness is %d.", (int)pop[maxPop].fitness);
115     printf("\nThe population's max weight is %d.\n", (int)pop[maxPop].weight);
116 }
117 
118 /**====================
119  * 用途:初始化种群   *
120  ====================**/
121 void initPop(){
122     int i, j;
123     double tmpWeight;
124     bool isPop = false;
125     // 生成一个祖宗种群
126     for( i = 0; i < POP_SIZE; i++ ){
127         while(!isPop){
128             // 如果不满足条件,则继续随机产生直到有相同的个体
129             for( j = 0; j < CHROM_SIZE; j++ ){
130                 oldPop[i].chrom[j] = rand()%2;    // 产生0/1的任意一个数
131                 oldPop[i].parent1 = 0;
132                 oldPop[i].parent2 = 0;
133                 oldPop[i].cross = 0;
134             }
135             // 选择重量小于背包容量的个体,满足条件的个体
136             
137             tmpWeight = calWeight( oldPop[i].chrom );
138             if( tmpWeight <= contain ){
139                 oldPop[i].fitness = calFit( oldPop[i].chrom );
140                 oldPop[i].weight = tmpWeight;
141                 oldPop[i].parent1 = 0;
142                 oldPop[i].parent2 = 0;
143                 oldPop[i].cross = 0;
144                 isPop = true;
145             }
146         }
147         isPop = false;
148     }
149 }
150 /**=========================================
151  * 参数:用于带入概率参数:"交叉"/"变异"       *
152  * 用途:概率选择试验                       *
153  =========================================**/
154 int excise( double probability ){
155     double pp;
156     pp = (double)(rand()%20001/20000.0);
157     if( pp <= probability ) 
158         return 1;
159     return 0;
160 }
161 
162 /**====================
163  * 用途:个体的选择   *
164  ====================**/
165 int    selection(int pop){
166     double wheelPos, randNumber,partsum = 0;
167     int i = 0;
168     // 轮赌法
169     randNumber=(rand()%2001)/2000.0;
170     wheelPos = randNumber*sumFitness;
171     do {
172         partsum += oldPop[i].fitness;
173         i++;
174     }while( ( partsum < wheelPos) && (i < POP_SIZE) );
175     return i-1;
176 }
177 
178 /**========================================
179  * 参数:父母染色体,即不被测试选到的染色体
180  * 用途:染色体交叉
181  * 返回值:1
182  *========================================*/
183 int crossOver(unsigned int *parent1, unsigned int *parent2, int i){
184     int j;                    // 基因组的基因位置
185     int crossPos;            // 交叉点位置
186     if( excise(PRO_CROSS) ){
187         crossPos = rand()%(CHROM_SIZE-1);    
188     }else{
189         crossPos = CHROM_SIZE - 1;
190     }
191     // 开始交叉
192     for( j = 0; j <= crossPos; j++ ){
193         newPop[i].chrom[j] = parent1[j];
194     }
195     for( j = crossPos+1; j < CHROM_SIZE; j++ ){
196         newPop[i].chrom[j] = parent2[j];
197     }
198     newPop[i].cross = crossPos;
199     return 1;
200 }
201 
202 /**==========================================
203  * 参数:染色体基因组的某一位置突变
204  * 用途:染色体基因突变
205  * 返回值:若突变,则突变结果,否则,原样返回
206  *=========================================*/
207 int mutation(unsigned int alleles){
208     if( excise(PRO_MUTATE) ){
209         // 满足突变概率,则此基因突变
210         alleles == 0? alleles = 1: alleles = 0;
211     }
212     return alleles;
213 }
214 
215 /**====================================================
216  * mate1,mate2是从种群中挑出的能来诞生新一代的个体位置
217  * 用途:种群群体更新
218  *=====================================================*/
219 void generation(){
220     unsigned int i, j, mate1, mate2; 
221     double tmpWeight = 0;
222     bool notGen;
223     for( i = 0; i < POP_SIZE; i++){
224         notGen = false;
225         // 需要繁殖
226         while( !notGen ){
227             // 选择:选择有几率产生优良后代的父母个体位置
228             mate1 = selection(i);
229             mate2 = selection(i+1);
230             // 交叉:产生新一代替换掉不符合生存条件的i
231             crossOver( oldPop[mate1].chrom, oldPop[mate2].chrom, i);
232             for( j = 0; j < CHROM_SIZE; j++ ){
233                 // 变异:新个体一个一个基因位置代入,看是否要突变
234                 newPop[i].chrom[j] = mutation(newPop[i].chrom[j]);
235             }
236             // 选择重量小于背包重量的个体,满足条件,则把价值给记录起来
237             tmpWeight = calWeight( newPop[i].chrom );
238             if( tmpWeight <= contain ){
239                 newPop[i].fitness = calFit( newPop[i].chrom );
240                 newPop[i].weight = tmpWeight;
241                 newPop[i].parent1 = mate1;
242                 newPop[i].parent2 = mate2;
243                 notGen = true;                // 不需要再产生子代
244             }
245         }
246     }
247 }
248 
249 void main(int argc, char* argv[]){
250     int gen, oldMaxPop, k;
251     double oldMax;
252     // 输入每个物品的重量和价值,背包容量
253     printf("Please input the products\' weights:\n");
254     for(int i = 0; i < CHROM_SIZE; i++ )
255         scanf("%d", &weight[i]);
256     printf("\nPlease input the products\' porfits:\n");
257     for(int j = 0; j < CHROM_SIZE; j++ )
258         scanf("%d", &profit[j]);    
259     contain = 1000;
260     gen = 0;                        // 代表第一代(主要用在输出显示上)
261 
262     srand( (unsigned)time(NULL) );    // 置随机种子
263     initPop();                        // 初始化种群
264     memcpy(&newPop,&oldPop,POP_SIZE*sizeof(struct population));
265     statistics( newPop );            // 产生新种群的信息(适应度等)
266     report( newPop, gen );
267     while( gen < GENERATION_NUM ){
268         gen += 1;
269         if( gen % 100 == 0 )
270             srand( (unsigned)time(NULL) );    // 每一百代产生新的随机数
271         oldMax = maxFitness;        // 将最大适应度给上一代
272         oldMaxPop = maxPop;            // 拥有最大适应度的个体位置给上一代
273         generation();
274         statistics( newPop );
275 
276         // 如果新的最大适应度小于上一代最大适应度,则上一代活着
277         if( maxFitness < oldMax ){
278             for( k = 0; k < CHROM_SIZE; k++ ){
279                 newPop[minPop].chrom[k] = oldPop[oldMaxPop].chrom[k];
280             }
281             newPop[minPop].fitness = oldPop[oldMaxPop].fitness;
282             newPop[minPop].parent1 = oldPop[oldMaxPop].parent1;
283             newPop[minPop].parent2 = oldPop[oldMaxPop].parent2;
284             newPop[minPop].cross = newPop[minPop].cross;
285             statistics(newPop);
286         }else if( maxFitness > oldMax ){
287             report(newPop, gen);
288         }
289          //保存新生代种群的信息到老一代种群信息空间
290         memcpy(&oldPop,&newPop,POP_SIZE*sizeof(struct population));
291     }
292 }

 

posted @ 2012-12-27 21:11  unfairworld  阅读(7924)  评论(0编辑  收藏  举报